using System;
using System.Collections.Generic;
using Oracle.ManagedDataAccess.Client;

namespace OracleInternal.Sharding
{
	internal class RangeMap<K, V> where K : IComparable<K>
	{
		private List<K> rangeMapKeys = new List<K>();

		private List<RangeMapValue<K, V>> rangeMapValues = new List<RangeMapValue<K, V>>();

		internal int Count => rangeMapKeys.Count;

		internal RangeMap()
		{
		}

		internal void Put(K keyFloor, K keyCeiling, V value)
		{
			List<V> list = new List<V>();
			list.Add(value);
			Put(keyFloor, keyCeiling, list);
		}

		internal void Put(K keyFloor, K keyCeiling, List<V> values)
		{
			if (keyFloor == null)
			{
				throw new ArgumentNullException("keyFloor", OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.SHARDING_INVALID_TOPOLOGY));
			}
			if (keyCeiling == null)
			{
				throw new ArgumentNullException("keyCeiling", OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.SHARDING_INVALID_TOPOLOGY));
			}
			if (values == null)
			{
				throw new ArgumentNullException("values", OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.SHARDING_INVALID_TOPOLOGY));
			}
			if (keyFloor.CompareTo(keyCeiling) > 0)
			{
				throw new ArgumentException(OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.SHARDING_INVALID_TOPOLOGY));
			}
			int num = FloorEntryIndex(keyFloor);
			int num2 = FloorEntryIndex(keyCeiling);
			if (num2 != -1 && rangeMapKeys[num2].CompareTo(keyCeiling) == 0)
			{
				num2--;
			}
			RangeMapValue<K, V> rangeMapValue = ((num == -1) ? null : rangeMapValues[num]);
			RangeMapValue<K, V> rangeMapValue2 = ((num2 == -1) ? null : rangeMapValues[num2]);
			K val = ((num == -1) ? keyFloor : ((rangeMapValue.KeyCeiling.CompareTo(keyFloor) > 0) ? rangeMapValue.KeyFloor : keyFloor));
			K val2 = ((num2 == -1) ? keyCeiling : ((rangeMapValue2.KeyCeiling.CompareTo(keyCeiling) >= 0) ? rangeMapValue2.KeyCeiling : keyCeiling));
			List<RangeMapValue<K, V>> list = Submap(val, val2);
			if (list.Count == 0)
			{
				Insert(val, val2, values);
			}
			else if (isOneSameRange(list, keyFloor, keyCeiling))
			{
				list[0].Values.AddRange(values);
			}
			else if (isOneLargerRange(list, keyFloor, keyCeiling))
			{
				RangeMapValue<K, V> rangeMapValue3 = list[0];
				RangeMap<K, V> rangeMap = null;
				if (rangeMapValue3.NestedRanges.Count == 0)
				{
					rangeMap = new RangeMap<K, V>();
					rangeMapValue3.NestedRanges.Add(rangeMap);
				}
				else
				{
					rangeMap = rangeMapValue3.NestedRanges[0];
				}
				rangeMap.Put(keyFloor, keyCeiling, values);
			}
			else if (isOneSmallerRange(list, keyFloor, keyCeiling))
			{
				RangeMap<K, V> rangeMap2 = new RangeMap<K, V>();
				rangeMap2.AddRange(list);
				RemoveSubmap(list);
				List<RangeMap<K, V>> list2 = new List<RangeMap<K, V>>();
				list2.Add(rangeMap2);
				Insert(val, val2, list2, values);
			}
			else
			{
				RangeMap<K, V> rangeMap3 = new RangeMap<K, V>();
				rangeMap3.AddRange(list);
				RemoveSubmap(list);
				RangeMap<K, V> rangeMap4 = new RangeMap<K, V>();
				rangeMap4.Add(keyFloor, keyCeiling, values);
				List<RangeMap<K, V>> list3 = new List<RangeMap<K, V>>();
				list3.Add(rangeMap3);
				list3.Add(rangeMap4);
				Insert(val, val2, list3, new List<V>());
			}
		}

		internal List<V> Get(K key)
		{
			RangeMapValue<K, V> rangeMapValue = FloorEntry(key);
			if (rangeMapValue != null)
			{
				K keyFloor = rangeMapValue.KeyFloor;
				RangeMapValue<K, V> rangeMapValue2 = rangeMapValue;
				if ((keyFloor.Equals(rangeMapValue2.KeyCeiling) && key.Equals(keyFloor)) || key.CompareTo(rangeMapValue2.KeyCeiling) < 0)
				{
					if (rangeMapValue2.NestedRanges.Count == 0)
					{
						return rangeMapValue2.Values;
					}
					List<V> list = new List<V>();
					list.AddRange(rangeMapValue2.Values);
					{
						foreach (RangeMap<K, V> nestedRange in rangeMapValue2.NestedRanges)
						{
							List<V> list2 = nestedRange.Get(key);
							if (list2 != null)
							{
								list.AddRange(list2);
							}
						}
						return list;
					}
				}
			}
			return null;
		}

		internal List<V> Get(K keyFloor, K keyCeiling)
		{
			RangeMapValue<K, V> rangeMapValue = FloorEntry(keyFloor);
			if (rangeMapValue != null)
			{
				K keyFloor2 = rangeMapValue.KeyFloor;
				RangeMapValue<K, V> rangeMapValue2 = rangeMapValue;
				if (keyFloor2.Equals(keyFloor) && rangeMapValue2.KeyCeiling.Equals(keyCeiling))
				{
					return rangeMapValue2.Values;
				}
				if (keyFloor2.CompareTo(keyFloor) <= 0 && rangeMapValue2.KeyCeiling.CompareTo(keyCeiling) >= 0)
				{
					foreach (RangeMap<K, V> nestedRange in rangeMapValue2.NestedRanges)
					{
						List<V> list = nestedRange.Get(keyFloor, keyCeiling);
						if (list != null && list.Count > 0)
						{
							return list;
						}
					}
				}
			}
			return null;
		}

		internal void Replace(K keyFloor, K keyCeiling, V oldValue, V newValue)
		{
			List<V> list = Get(keyFloor, keyCeiling);
			if (list == null || list.Count <= 0)
			{
				return;
			}
			for (int i = 0; i < list.Count; i++)
			{
				if (list[i].Equals(oldValue))
				{
					list.RemoveAt(i);
					list.Insert(i, newValue);
				}
			}
		}

		internal void Remove(K keyFloor, K keyCeiling, V value)
		{
			int num = FloorEntryIndex(keyFloor);
			RangeMapValue<K, V> rangeMapValue = ((num == -1) ? null : rangeMapValues[num]);
			if (rangeMapValue == null)
			{
				return;
			}
			K keyFloor2 = rangeMapValue.KeyFloor;
			RangeMapValue<K, V> rangeMapValue2 = rangeMapValue;
			if (keyFloor2.CompareTo(keyFloor) <= 0 && rangeMapValue2.KeyCeiling.CompareTo(keyCeiling) >= 0)
			{
				int num2 = 0;
				while (num2 < rangeMapValue2.NestedRanges.Count)
				{
					RangeMap<K, V> rangeMap = rangeMapValue2.NestedRanges[num2];
					rangeMap.Remove(keyFloor, keyCeiling, value);
					if (rangeMap.Count == 0)
					{
						rangeMapValue2.NestedRanges.RemoveAt(num2);
					}
					else
					{
						num2++;
					}
				}
			}
			if (!keyFloor2.Equals(keyFloor) || !rangeMapValue2.KeyCeiling.Equals(keyCeiling))
			{
				return;
			}
			if (value != null)
			{
				rangeMapValue2.Values.Remove(value);
			}
			else
			{
				rangeMapValue2.Values.Clear();
			}
			if (rangeMapValue2.Values.Count != 0)
			{
				return;
			}
			if (rangeMapValue2.NestedRanges.Count == 0)
			{
				RemoveAt(num);
			}
			else if (1 == rangeMapValue2.NestedRanges.Count)
			{
				RangeMap<K, V> rangeMap2 = rangeMapValue2.NestedRanges[0];
				RemoveAt(num);
				for (int num3 = rangeMap2.Count - 1; num3 >= 0; num3--)
				{
					rangeMapKeys.Insert(num, rangeMap2.rangeMapKeys[num3]);
					rangeMapValues.Insert(num, rangeMap2.rangeMapValues[num3]);
					rangeMap2.RemoveAt(num3);
				}
			}
		}

		internal void Add(K keyFloor, K keyCeiling, List<V> values)
		{
			Add(keyFloor, keyCeiling, new List<RangeMap<K, V>>(), values);
		}

		internal void Add(K keyFloor, K keyCeiling, List<RangeMap<K, V>> nestedRanges, List<V> values)
		{
			RangeMapValue<K, V> item = new RangeMapValue<K, V>(keyFloor, keyCeiling, nestedRanges, new List<V>(values));
			rangeMapKeys.Add(keyFloor);
			rangeMapValues.Add(item);
		}

		internal void Insert(K keyFloor, K keyCeiling, List<V> values)
		{
			Insert(keyFloor, keyCeiling, new List<RangeMap<K, V>>(), values);
		}

		internal void Insert(K keyFloor, K keyCeiling, List<RangeMap<K, V>> nestedRanges, List<V> values)
		{
			RangeMapValue<K, V> value = new RangeMapValue<K, V>(keyFloor, keyCeiling, nestedRanges, new List<V>(values));
			Insert(keyFloor, value);
		}

		internal void Insert(K key, RangeMapValue<K, V> value)
		{
			int num = rangeMapKeys.BinarySearch(key);
			if (num < 0)
			{
				rangeMapKeys.Insert(~num, key);
				rangeMapValues.Insert(~num, value);
			}
		}

		internal void Remove(K key)
		{
			int num = rangeMapKeys.BinarySearch(key);
			if (num >= 0)
			{
				rangeMapKeys.RemoveAt(num);
				rangeMapValues.RemoveAt(num);
			}
		}

		internal void AddRange(List<RangeMapValue<K, V>> range)
		{
			foreach (RangeMapValue<K, V> item in range)
			{
				rangeMapKeys.Add(item.KeyFloor);
				rangeMapValues.Add(item);
			}
		}

		internal void RemoveRange(int index, int count)
		{
			rangeMapKeys.RemoveRange(index, count);
			rangeMapValues.RemoveRange(index, count);
		}

		internal void RemoveAt(int index)
		{
			rangeMapKeys.RemoveAt(index);
			rangeMapValues.RemoveAt(index);
		}

		internal void Clear()
		{
			rangeMapKeys.Clear();
			rangeMapValues.Clear();
		}

		internal List<RangeMapValue<K, V>> Submap(K fromKey, K toKey)
		{
			List<RangeMapValue<K, V>> list = new List<RangeMapValue<K, V>>();
			int num = 0;
			int num2 = Count - 1;
			int num3 = 0;
			if (fromKey != null)
			{
				num3 = rangeMapKeys.BinarySearch(fromKey);
				num = ((num3 >= 0) ? num3 : (~num3));
			}
			if (toKey != null)
			{
				num3 = rangeMapKeys.BinarySearch(toKey);
				num2 = ((fromKey == null || fromKey.CompareTo(toKey) != 0) ? ((num3 >= 0) ? (num3 - 1) : (~num3 - 1)) : ((num3 >= 0) ? num3 : (~num3 - 1)));
			}
			for (int i = num; i <= num2; i++)
			{
				list.Add(rangeMapValues[i]);
			}
			return list;
		}

		internal void RemoveSubmap(List<RangeMapValue<K, V>> subMap)
		{
			if (subMap != null && subMap.Count != 0)
			{
				int num = rangeMapKeys.BinarySearch(subMap[0].KeyFloor);
				if (num >= 0)
				{
					RemoveRange(num, subMap.Count);
				}
			}
		}

		private bool isOneSameRange(List<RangeMapValue<K, V>> subMap, K keyFloor, K keyCeiling)
		{
			if (subMap == null)
			{
				throw new ArgumentNullException("subMap", OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.SHARDING_INVALID_TOPOLOGY));
			}
			if (keyFloor == null)
			{
				throw new ArgumentNullException("keyFloor", OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.SHARDING_INVALID_TOPOLOGY));
			}
			if (keyCeiling == null)
			{
				throw new ArgumentNullException("keyCeiling", OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.SHARDING_INVALID_TOPOLOGY));
			}
			if (keyFloor.CompareTo(keyCeiling) > 0)
			{
				throw new ArgumentException(OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.SHARDING_INVALID_TOPOLOGY));
			}
			if (1 != subMap.Count)
			{
				return false;
			}
			RangeMapValue<K, V> rangeMapValue = subMap[0];
			if (rangeMapValue.KeyFloor.CompareTo(keyFloor) == 0 && rangeMapValue.KeyCeiling.CompareTo(keyCeiling) == 0)
			{
				return true;
			}
			return false;
		}

		private bool isOneLargerRange(List<RangeMapValue<K, V>> subMap, K keyFloor, K keyCeiling)
		{
			if (subMap == null)
			{
				throw new ArgumentNullException("subMap", OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.SHARDING_INVALID_TOPOLOGY));
			}
			if (keyFloor == null)
			{
				throw new ArgumentNullException("keyFloor", OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.SHARDING_INVALID_TOPOLOGY));
			}
			if (keyCeiling == null)
			{
				throw new ArgumentNullException("keyCeiling", OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.SHARDING_INVALID_TOPOLOGY));
			}
			if (keyFloor.CompareTo(keyCeiling) > 0)
			{
				throw new ArgumentException(OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.SHARDING_INVALID_TOPOLOGY));
			}
			if (1 != subMap.Count)
			{
				return false;
			}
			RangeMapValue<K, V> rangeMapValue = subMap[0];
			if ((keyFloor.CompareTo(rangeMapValue.KeyFloor) >= 0 && keyCeiling.CompareTo(rangeMapValue.KeyCeiling) < 0) || (keyFloor.CompareTo(rangeMapValue.KeyFloor) > 0 && keyCeiling.CompareTo(rangeMapValue.KeyCeiling) <= 0))
			{
				return true;
			}
			return false;
		}

		private bool isOneSmallerRange(List<RangeMapValue<K, V>> subMap, K keyFloor, K keyCeiling)
		{
			if (subMap == null)
			{
				throw new ArgumentNullException("subMap", OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.SHARDING_INVALID_TOPOLOGY));
			}
			if (keyFloor == null)
			{
				throw new ArgumentNullException("keyFloor", OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.SHARDING_INVALID_TOPOLOGY));
			}
			if (keyCeiling == null)
			{
				throw new ArgumentNullException("keyCeiling", OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.SHARDING_INVALID_TOPOLOGY));
			}
			if (keyFloor.CompareTo(keyCeiling) > 0)
			{
				throw new ArgumentException(OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.SHARDING_INVALID_TOPOLOGY));
			}
			if (subMap.Count == 0)
			{
				return false;
			}
			RangeMapValue<K, V> rangeMapValue = subMap[0];
			RangeMapValue<K, V> rangeMapValue2 = subMap[subMap.Count - 1];
			if (keyFloor.CompareTo(rangeMapValue.KeyFloor) <= 0 && keyCeiling.CompareTo(rangeMapValue2.KeyCeiling) >= 0)
			{
				return true;
			}
			return false;
		}

		internal RangeMapValue<K, V> FloorEntry(K key)
		{
			int num = FloorEntryIndex(key);
			if (num != -1)
			{
				return rangeMapValues[num];
			}
			return null;
		}

		internal int FloorEntryIndex(K key)
		{
			if (Count == 0)
			{
				return -1;
			}
			int num = rangeMapKeys.BinarySearch(key, null);
			if (num >= 0)
			{
				return num;
			}
			return ~num - 1;
		}
	}
}
